"""Classes for Procedure, Function and View are defined in this file"""

from database import DataBase
import pydot

class SQLObject(object):
    SQLObjectType = "SQLObject"
    codeIndex = None #is the index of the code in the table
    nodeColor = "Black"

    def __init__(self, database, Name):
        self.database = database
        self.cursor = self.database.cursor

        self.Name = Name
        self.code = ""

        self.ReferencedObjects = []
        self.ReferencedNodes = []
        
        self.Node = pydot.Node(str(self))
        self.Node.set_color(1, self.nodeColor)
        #self.Edges = None

    def __repr__(self):
        return "%s %s" %(self.SQLObjectType, self.Name)
    
    def getInfo(self):
        """Loads the definition of the object"""
        data = self.database.getDefinitionForName(self.Name, self.SQLObjectType)[0]
        return data

    def getReferences(self):
        """Gets a list of objects that this object references"""
        lines = self.Code.splitlines()
        callingLines = (line for line in lines if line.count('(') > 0) #get all lines that contain a ( and thus possibly call a routine

        referencedNames = [self.findReferencedName(line) for line in callingLines]
        return referencedNames[1:] #skip the first one, which is a reference to itself

    def findReferencedName(self, line):
        #finds the name of the routine that is referenced in the given line
        funcNameEnd = line.find("(") #after a function definition come the parameters, so a ( is used
        funcNameStart = line.rfind(" ",0,funcNameEnd) #the functionname starts after `databasename`.` rfind is used to find the last index of that substring

        funcName = line[funcNameStart+1:funcNameEnd]
        funcName = funcName.strip("`")
        if funcName != '':
            return funcName

    def getCode(self):
        if not self.code:
            self.code = self.getInfo()[self.codeIndex]

        return self.code

    Code = property(getCode)

    References = property(getReferences)

class SQLRoutine(SQLObject):
    """Base class for stored routines."""
    SQLObjectType = "Routine"
    codeIndex = 2 #2 is the index of the code in the table
    nodeColor = "Blue"

    def __init__(self, database, Name):
        SQLObject.__init__(self, database, Name)

class SQLFunction(SQLRoutine):
    """Class for stored functions."""
    SQLObjectType = "Function"
    nodeColor = "Green"

    def __init__(self, database, Name):
        SQLRoutine.__init__(self, database, Name)

class SQLProcedure(SQLRoutine):
    """Class for stored procedures."""
    SQLObjectType = "Procedure"

    def __init__(self, database, Name):
        SQLRoutine.__init__(self, database, Name)

class SQLView(SQLObject):
    """class for SQL views"""
    SQLObjectType = "View"
    codeIndex = 1
    nodeColor = "Purple"
    
    def __init__(self, database, Name):
       SQLObject.__init__(self, database, Name)

    def getReferences(self):
        """Gets a list of objects that this object references"""
        lines = self.Code.split(",")
        callingLines = [line for line in lines if line.count('`(`') > 0] #get all lines that contain a ( and thus possibly call a routine

        referencedNames = [self.findReferencedName(line) for line in callingLines]
        return referencedNames

    References = property(getReferences)

def getSQLObjects(database):
    TypeToClassMapping = {"VIEW": SQLView, "FUNCTION": SQLFunction, "PROCEDURE": SQLProcedure}
    objects = {}
    for name, sqlobjecttype in d.getRoutineNamesFromDB().iteritems():
        objects[name] = TypeToClassMapping[sqlobjecttype](d, name)
    return objects

def NamesToObjects(objects):
    """Objects is a dictionary with setup name:SQLObject"""
    for name, sqlobject in objects.items():
        try:
            objrefs = [objects[refname] for refname in sqlobject.References]
            objects[name].ReferencedObjects = objrefs
        except KeyError, ke:
            print "Key %s not Found" %ke
    return objects

def genReferenceDictFromObjects(objects):
    """objects: a dictionary with setup name:SQLObject. The SQLObject attribute ReferencedObjects has already been set."""
    references = {}
    nodeReferences = {}
    for obj in objects.values():
        references[str(obj)] = [str(ref) for ref in obj.ReferencedObjects]
        nodeReferences[obj.Node] = [ref.Node for ref in obj.ReferencedObjects]
    
    return references, nodeReferences

def genEdgesForDot(references):
    """references is a dict with setup Node:[Node, Node...]"""
    edges = []
    for source, destinations in references.items():
        for dst in destinations:#[1:]: #skip the first one, which is a reference to itself.
            if dst and (dst in references.keys()): #if not '' and in the list of routines and aren't ignored
                print "Processing call from %s to %s..." %(source, dst)
                edge = pydot.Edge(source, dst)
                edges.append(edge)
    return edges

if __name__ == "__main__":
    d = DataBase('localhost', 'root', 'ProtossPass', 'protossmaindb')
    objects = getSQLObjects(d)
    print objects

    n =  objects['ReCalcAllStartFinishTimes'].Node
    print n

    objects = NamesToObjects(objects)

    from sql_call_graph_generator import *
    references, nodeReferences = genReferenceDictFromObjects(objects)
    edges = genEdgesForDot(nodeReferences)
    p = genGraph(edges, "classes graph.png")

    #Edges moeten Edges(src_naam, dst_naam) zijn. Aan de Graph worden de Node toegevoegd, ik hoop dat graphviz dan zelf de goede nodes met kleuren etc pakt. 
